{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Linear Regression Example\n",
    "\n",
    "Linear regression implementation with TensorFlow v2 library.\n",
    "\n",
    "This example is using a low-level approach to better understand all mechanics behind the training process.\n",
    "\n",
    "- Author: Aymeric Damien\n",
    "- Project: https://github.com/aymericdamien/TensorFlow-Examples/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "import numpy as np\n",
    "rng = np.random"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Parameters.\n",
    "learning_rate = 0.01\n",
    "training_steps = 1000\n",
    "display_step = 50"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Training Data.\n",
    "X = np.array([3.3,4.4,5.5,6.71,6.93,4.168,9.779,6.182,7.59,2.167,\n",
    "              7.042,10.791,5.313,7.997,5.654,9.27,3.1])\n",
    "Y = np.array([1.7,2.76,2.09,3.19,1.694,1.573,3.366,2.596,2.53,1.221,\n",
    "              2.827,3.465,1.65,2.904,2.42,2.94,1.3])\n",
    "n_samples = X.shape[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Weight and Bias, initialized randomly.\n",
    "W = tf.Variable(rng.randn(), name=\"weight\")\n",
    "b = tf.Variable(rng.randn(), name=\"bias\")\n",
    "\n",
    "# Linear regression (Wx + b).\n",
    "def linear_regression(x):\n",
    "    return W * x + b\n",
    "\n",
    "# Mean square error.\n",
    "def mean_square(y_pred, y_true):\n",
    "    return tf.reduce_sum(tf.pow(y_pred-y_true, 2)) / (2 * n_samples)\n",
    "\n",
    "# Stochastic Gradient Descent Optimizer.\n",
    "optimizer = tf.optimizers.SGD(learning_rate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Optimization process. \n",
    "def run_optimization():\n",
    "    # Wrap computation inside a GradientTape for automatic differentiation.\n",
    "    with tf.GradientTape() as g:\n",
    "        pred = linear_regression(X)\n",
    "        loss = mean_square(pred, Y)\n",
    "\n",
    "    # Compute gradients.\n",
    "    gradients = g.gradient(loss, [W, b])\n",
    "    \n",
    "    # Update W and b following gradients.\n",
    "    optimizer.apply_gradients(zip(gradients, [W, b]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "step: 50, loss: 0.210631, W: 0.458940, b: -0.670898\n",
      "step: 100, loss: 0.195340, W: 0.446725, b: -0.584301\n",
      "step: 150, loss: 0.181797, W: 0.435230, b: -0.502807\n",
      "step: 200, loss: 0.169803, W: 0.424413, b: -0.426115\n",
      "step: 250, loss: 0.159181, W: 0.414232, b: -0.353942\n",
      "step: 300, loss: 0.149774, W: 0.404652, b: -0.286021\n",
      "step: 350, loss: 0.141443, W: 0.395636, b: -0.222102\n",
      "step: 400, loss: 0.134064, W: 0.387151, b: -0.161949\n",
      "step: 450, loss: 0.127530, W: 0.379167, b: -0.105341\n",
      "step: 500, loss: 0.121742, W: 0.371652, b: -0.052068\n",
      "step: 550, loss: 0.116617, W: 0.364581, b: -0.001933\n",
      "step: 600, loss: 0.112078, W: 0.357926, b: 0.045247\n",
      "step: 650, loss: 0.108058, W: 0.351663, b: 0.089647\n",
      "step: 700, loss: 0.104498, W: 0.345769, b: 0.131431\n",
      "step: 750, loss: 0.101345, W: 0.340223, b: 0.170753\n",
      "step: 800, loss: 0.098552, W: 0.335003, b: 0.207759\n",
      "step: 850, loss: 0.096079, W: 0.330091, b: 0.242583\n",
      "step: 900, loss: 0.093889, W: 0.325468, b: 0.275356\n",
      "step: 950, loss: 0.091949, W: 0.321118, b: 0.306198\n",
      "step: 1000, loss: 0.090231, W: 0.317024, b: 0.335223\n"
     ]
    }
   ],
   "source": [
    "# Run training for the given number of steps.\n",
    "for step in range(1, training_steps + 1):\n",
    "    # Run the optimization to update W and b values.\n",
    "    run_optimization()\n",
    "    \n",
    "    if step % display_step == 0:\n",
    "        pred = linear_regression(X)\n",
    "        loss = mean_square(pred, Y)\n",
    "        print(\"step: %i, loss: %f, W: %f, b: %f\" % (step, loss, W.numpy(), b.numpy()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Graphic display\n",
    "plt.plot(X, Y, 'ro', label='Original data')\n",
    "plt.plot(X, np.array(W * X + b), label='Fitted line')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 2",
   "language": "python",
   "name": "python2"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.15"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
